/* eslint-disable unicorn/prefer-spread */
export class CanvasDrawer {
  private canvas: HTMLCanvasElement;
  private ctx!: CanvasRenderingContext2D;
  private designWidth: number;
  private dpr: number = 1;
  private scale!: number;

  constructor({ canvas, designWidth = 375, size }: CanvasDrawerOptions) {
    this.designWidth = designWidth;
    this.canvas = canvas;
    this.resize(size || canvas.getBoundingClientRect());
  }

  /** 清空画布 */
  clear() {
    this.ctx.clearRect(
      0,
      0,
      this.canvas.width,
      this.canvas.height,
      // this.canvas.width / (this.dpr * this.scale),
      // this.canvas.height / (this.dpr * this.scale),
    );
  }

  /** 绘制卡片（白色矩形 + 标题 + 内容） */
  drawCardWithText(options: DrawCardOptions): { bottomY: number; height: number } {
    const rectHeight = this.measureCardHeight(options);
    const {
      x,
      y,
      width,
      radius = { tl: 12, tr: 12, bl: 12 },
      title,
      titleFont = 'bold 18px sans-serif',
      titleLineHeight = 24,
      titleColor = '#000',
      content,
      contentFont = '16px sans-serif',
      contentLineHeight = 22,
      contentColor = '#666',
      gap = 8,
      padding,
      shadow,
    } = options;

    const p = this.normalizePadding(padding);

    // 阴影
    if (shadow) {
      this.ctx.save();
      this.ctx.shadowColor = shadow.color ?? 'rgba(0,0,0,0.15)';
      this.ctx.shadowBlur = shadow.blur ?? 8;
      this.ctx.shadowOffsetX = shadow.offsetX ?? 0;
      this.ctx.shadowOffsetY = shadow.offsetY ?? 4;
    }

    this.drawRoundRect(x, y, width, rectHeight, { ...radius, br: 0 }, '#fff', undefined);

    if (shadow) this.ctx.restore();

    // 画标题
    this.drawTextWithEllipsis({
      text: title,
      x: x + p.left,
      y: y + p.top + titleLineHeight,
      maxWidth: width - p.left - p.right,
      lineHeight: titleLineHeight,
      maxLines: 2,
      font: titleFont,
      color: titleColor,
      align: 'center',
      padding: { left: 0, right: 0 }, // 内部不再重复 padding
    });

    // 画内容
    const titleHeight = this.measureTextHeight(title, width - p.left - p.right, titleLineHeight, 2);
    this.drawTextWithEllipsis({
      text: content,
      x: x + p.left,
      y: y + p.top + titleHeight + gap + contentLineHeight,
      maxWidth: width - p.left - p.right,
      lineHeight: contentLineHeight,
      maxLines: 12,
      font: contentFont,
      color: contentColor,
      padding: { left: 0, right: 0 },
    });

    return { height: rectHeight, bottomY: y + rectHeight };
  }

  /** 绘制图片 */
  async drawImage(
    src: HTMLImageElement | ImageBitmap | string,
    x: number,
    y: number,
    width?: number,
    height?: number,
  ) {
    const img: HTMLImageElement | ImageBitmap =
      typeof src === 'string' ? await this.loadImage(src) : src;

    const drawWidth = width ?? ('width' in img ? img.width : this.canvas.width);
    const drawHeight = height ?? ('height' in img ? img.height : this.canvas.height);

    this.ctx.drawImage(img as CanvasImageSource, x, y, drawWidth, drawHeight);
  }

  /** 绘制清晰 1px 直线 */
  drawLine(x1: number, y1: number, x2: number, y2: number, color: string = '#000') {
    this.ctx.beginPath();
    this.ctx.strokeStyle = color;
    this.ctx.moveTo(x1, y1 + 0.5);
    this.ctx.lineTo(x2, y2 + 0.5);
    this.ctx.stroke();
  }

  /** 绘制矩形 */
  drawRect(x: number, y: number, width: number, height: number, color: string = '#000') {
    this.ctx.fillStyle = color;
    this.ctx.fillRect(x, y, width, height);
  }

  /** 绘制圆角矩形 */
  drawRoundRect(
    x: number,
    y: number,
    width: number,
    height: number,
    radii: number | { bl?: number; br?: number; tl?: number; tr?: number } = 0,
    fillColor?: string,
    strokeColor?: string,
  ) {
    const { ctx } = this;
    const radius =
      typeof radii === 'number'
        ? { tl: radii, tr: radii, br: radii, bl: radii }
        : { tl: 0, tr: 0, br: 0, bl: 0, ...radii };

    ctx.beginPath();
    ctx.moveTo(x + radius.tl, y);
    ctx.lineTo(x + width - radius.tr, y);
    ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr);
    ctx.lineTo(x + width, y + height - radius.br);
    ctx.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height);
    ctx.lineTo(x + radius.bl, y + height);
    ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl);
    ctx.lineTo(x, y + radius.tl);
    ctx.quadraticCurveTo(x, y, x + radius.tl, y);
    ctx.closePath();

    if (fillColor) {
      ctx.fillStyle = fillColor;
      ctx.fill();
    }
    if (strokeColor) {
      ctx.strokeStyle = strokeColor;
      ctx.stroke();
    }
  }

  /** 绘制普通文本（不省略、不自动换行） */
  drawText(options: {
    align?: 'center' | 'left' | 'right';
    baseline?: CanvasTextBaseline;
    color?: string;
    font?: string;
    maxWidth?: number; // 可选，传了会限制绘制宽度
    text: string;
    x: number;
    y: number;
  }) {
    const {
      text,
      x,
      y,
      color = '#000',
      font = '16px sans-serif',
      align = 'left',
      baseline = 'alphabetic',
      maxWidth,
    } = options;

    this.ctx.fillStyle = color;
    this.ctx.font = font;
    this.ctx.textAlign = align;
    this.ctx.textBaseline = baseline;

    if (maxWidth) {
      this.ctx.fillText(text, x, y, maxWidth);
    } else {
      this.ctx.fillText(text, x, y);
    }
  }

  /** 绘制文本，超出多行显示省略号，可水平对齐，支持 padding */
  drawTextWithEllipsis(options: {
    align?: 'center' | 'left' | 'right';
    baseline?: CanvasTextBaseline;
    color?: string;
    font?: string;
    lineHeight: number;
    maxLines?: number;
    maxWidth: number;
    padding?: number | Padding;
    text: string;
    x: number;
    y: number;
  }) {
    const {
      text,
      x,
      y,
      maxWidth,
      lineHeight,
      maxLines = 1,
      color = '#000',
      baseline = 'alphabetic',
      font = '16px sans-serif',
      align = 'left',
      padding,
    } = options;

    const p = this.normalizePadding(padding);

    this.ctx.fillStyle = color;
    this.ctx.textBaseline = baseline;
    this.ctx.font = font;

    const words = text.split('');
    let line = '';
    let currY = y;
    let lineCount = 0;

    const effectiveWidth = maxWidth - p.left - p.right;

    for (const word of words) {
      const testLine = line + word;
      const testWidth = this.ctx.measureText(testLine).width;

      if (testWidth > effectiveWidth && line !== '') {
        lineCount++;
        if (lineCount === maxLines) {
          let truncated = line;
          while (
            this.ctx.measureText(`${truncated}...`).width > effectiveWidth &&
            truncated.length > 0
          ) {
            truncated = truncated.slice(0, -1);
          }

          const offsetX =
            align === 'center'
              ? (effectiveWidth - this.ctx.measureText(`${truncated}...`).width) / 2
              : align === 'right'
                ? effectiveWidth - this.ctx.measureText(`${truncated}...`).width
                : 0;

          this.ctx.fillText(`${truncated}...`, x + p.left + offsetX, currY);
          return;
        } else {
          const offsetX =
            align === 'center'
              ? (effectiveWidth - this.ctx.measureText(line).width) / 2
              : align === 'right'
                ? effectiveWidth - this.ctx.measureText(line).width
                : 0;

          this.ctx.fillText(line, x + p.left + offsetX, currY);
          line = word;
          currY += lineHeight;
        }
      } else {
        line = testLine;
      }
    }

    // 绘制最后一行
    if (lineCount < maxLines && line) {
      const offsetX =
        align === 'center'
          ? (effectiveWidth - this.ctx.measureText(line).width) / 2
          : align === 'right'
            ? effectiveWidth - this.ctx.measureText(line).width
            : 0;

      this.ctx.fillText(line, x + p.left + offsetX, currY);
    }
  }

  getCurrentDayInfo() {
    const today = new Date();

    // 英文月份缩写数组
    const months = [
      'JAN',
      'FEB',
      'MAR',
      'APR',
      'MAY',
      'JUN',
      'JUL',
      'AUG',
      'SEP',
      'OCT',
      'NOV',
      'DEC',
    ];

    return months[today.getMonth()]!; // 获取大写缩写
  }

  /** 计算卡片所需高度 */
  measureCardHeight(options: Omit<DrawCardOptions, 'x' | 'y'>): number {
    const {
      width,
      padding,
      title,
      titleFont = 'bold 18px sans-serif',
      titleLineHeight = 24,
      content,
      contentFont = '16px sans-serif',
      contentLineHeight = 22,
      gap = 8,
    } = options;

    const p = this.normalizePadding(padding);

    this.ctx.font = titleFont;
    const titleHeight = this.measureTextHeight(title, width - p.left - p.right, titleLineHeight, 2);

    this.ctx.font = contentFont;
    const contentHeight = this.measureTextHeight(
      content,
      width - p.left - p.right,
      contentLineHeight,
      12,
    );

    return p.top + titleHeight + gap + contentHeight + p.bottom;
  }

  /** 计算多行文本高度 */
  measureTextHeight(text: string, maxWidth: number, lineHeight: number, maxLines?: number): number {
    const words = text.split('');
    let line = '';
    let lineCount = 0;

    for (const word of words) {
      const testLine = line + word;
      const testWidth = this.ctx.measureText(testLine).width;

      if (testWidth > maxWidth && line !== '') {
        lineCount++;
        if (maxLines && lineCount >= maxLines) {
          return lineCount * lineHeight;
        }
        line = word;
      } else {
        line = testLine;
      }
    }

    if (line) lineCount++;

    return lineCount * lineHeight;
  }

  async renderCanvas({
    content,
    title,
    bg,
    logo,
    dateBg,
    qr,
  }: {
    bg: string;
    content?: string;
    dateBg: string;
    logo: string;
    qr: string;
    title: string;
  }) {
    const sizeWidth = 1040;
    const cardParams: DrawCardOptions = {
      x: 75,
      y: 235,
      width: 890,
      title,
      content: content || '没有选中内容',
      titleFont: 'bold 54px sans-serif',
      contentFont: '40px sans-serif',
      titleLineHeight: 70,
      gap: 50,
      contentLineHeight: 60,
      padding: {
        bottom: 50,
        top: 50,
        left: 50,
        right: 50,
      },
      radius: {
        tl: 30,
        tr: 30,
        bl: 30,
      },
    };
    const top = 235;
    // 1️⃣ 先计算需要的总高度
    const cardHeight = this.measureCardHeight(cardParams);

    // 2️⃣ 再调整画布大小（比如需要容纳多个卡片）
    this.resize({ width: 1040, height: cardHeight + top + 363 });

    await this.drawImage(bg, 0, 0, 1040, 1662);

    this.drawRoundRect(
      cardParams.x,
      cardParams.y + 16,
      cardParams.width,
      cardHeight,
      { ...cardParams.radius, br: 0 },
      '#F9E4DC',
      undefined,
    );
    this.drawCardWithText(cardParams);

    await this.drawImage(logo, 75, cardHeight + top + 110, 292, 109);
    await this.drawImage(dateBg, 75, 36, 142, 159);

    this.drawText({
      text: String(new Date().getDate()),
      x: 146,
      y: 56,
      maxWidth: sizeWidth,
      font: 'bold 80px DIN-Bold,sans-serif',
      baseline: 'top',
      color: '#1D1D1D',
      align: 'center',
    });

    this.drawText({
      text: this.getCurrentDayInfo(),
      x: 146,
      y: 127 + 15,
      maxWidth: sizeWidth,
      font: 'bold 30px sans-serif',
      baseline: 'top',
      color: '#959595',
      align: 'center',
    });

    this.drawText({
      text: '识别二维码 查看更多',
      x: 75,
      y: cardHeight + top + 22 + 232,
      maxWidth: sizeWidth,
      font: '32px sans-serif',
      baseline: 'top',
      color: '#666',
    });

    // 绘制二维码
    await this.drawImage(qr, 735, cardHeight + top + 82, 230, 230);
  }

  /** 调整画布尺寸 */
  resize(size: { height: number; width: number }) {
    // this.scale = size.width / this.designWidth;
    this.canvas.width = size.width; //* this.dpr;
    this.canvas.height = size.height; //* this.dpr;

    const ctx = this.canvas.getContext('2d');
    if (!ctx) throw new Error('CanvasRenderingContext2D not supported');
    this.ctx = ctx;

    // this.ctx.scale(this.dpr * this.scale, this.dpr * this.scale);
  }

  /** 异步加载图片 */
  private loadImage(src: string): Promise<HTMLImageElement> {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.crossOrigin = 'anonymous';
      img.addEventListener('load', () => resolve(img));
      img.onerror = reject;
      img.src = src;
    });
  }

  /** 将 padding 统一成四方向 */
  private normalizePadding(padding?: number | Padding): Required<Padding> {
    if (typeof padding === 'number' || padding === undefined) {
      const val = padding ?? 0;
      return { top: val, right: val, bottom: val, left: val };
    } else {
      return {
        top: padding.top ?? 0,
        right: padding.right ?? 0,
        bottom: padding.bottom ?? 0,
        left: padding.left ?? 0,
      };
    }
  }
}

interface CanvasDrawerOptions {
  canvas: HTMLCanvasElement;
  designWidth?: number;
  size?: { height: number; width: number };
}

export interface DrawCardOptions {
  x: number;
  y: number;
  width: number;
  padding?: number | Padding;
  radius?: { bl?: number; tl?: number; tr?: number };
  title: string;
  titleFont?: string;
  titleLineHeight?: number;
  titleColor?: string;
  content: string;
  contentFont?: string;
  contentLineHeight?: number;
  contentColor?: string;
  gap?: number;
  shadow?: {
    blur?: number;
    color?: string;
    offsetX?: number;
    offsetY?: number;
  };
}

interface Padding {
  top?: number;
  right?: number;
  bottom?: number;
  left?: number;
}
